﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Reflection;
using DevelopStudio.Basic;
using DevelopStudio.Control.Basic;
using System.Globalization;

namespace DevelopStudio.Control.Commit
{
    public partial class CommitForm : ChangeListFormBase
    {
        private Dictionary<TreeNode, ChangelistPageControl> controlHistory = new Dictionary<TreeNode, ChangelistPageControl>();

        private AppEnum.WizardStep wizardStep;

        private bool selectedByControl = false;

        public AppEnum.WizardStep WizardStep
        {
            get
            {
                return wizardStep;
            }
            set
            {
                if (wizardStep != value)
                {
                    wizardStep = value;
                }

                this.ChangeButtonStatus(wizardStep);
                this.ProcessWizard(wizardStep);
            }
        }

        private TreeNode current;

        public TreeNode Current
        {
            get
            {
                return this.current;
            }

            private set
            {
                if (this.current != value)
                {
                    this.current = value;
                    this.ChangeMainControl(this.current);
                    this.ChangeWizardStep();
                    this.treeViewNav.Select();
                    this.selectedByControl = true;
                    this.treeViewNav.SelectedNode = this.current;
                }
            }
        }

        private CommitForm()
        {
            InitializeComponent();
        }

        public CommitForm(ChangelistInfo changelistInfo)
        {
            InitializeComponent();
            this.ChangelistInfo = changelistInfo;
        }

        private void ProcessConfirm()
        {
            GetSummary();

            ActionsSummaryWizardPageControl summaryControl = controlHistory[this.current] as ActionsSummaryWizardPageControl;

            if (summaryControl != null)
            {
                summaryControl.OnActivated();
            }
        }

        private void ProcessProgress()
        {
            ProgressPageControl progressControl = controlHistory[this.current] as ProgressPageControl;

            if (progressControl != null)
            {
                progressControl.StartProgress();
            }

            try
            {
                foreach (ChangelistPageControl page in this.controlHistory.Values)
                {
                    page.ApplyChanges();
                }
            }
            catch(Exception ex)
            {
                SmsSummaryAction exceptionAction = new SmsSummaryAction(string.Empty, string.Format(CultureInfo.CurrentCulture, ex.Message, Text));
                exceptionAction.Status = SmsSummaryAction.ActionStatus.CompleteWithErrors;
                items.Insert(0, exceptionAction);

                if (progressControl != null)
                {
                    progressControl.StopProgress();
                }

                return;
            }

            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;
            worker.RunWorkerAsync();
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                foreach (ChangelistPageControl page in this.controlHistory.Values)
                {
                    if (page.PostApply() == false)
                    {
                        e.Cancel = true;
                        return;
                    }

                    // Test
                    //if (page.TestPostChanges() == false)
                    //{
                    //    e.Cancel = true;
                    //    return;
                    //}
                }
            }
            catch(Exception ex)
            {
                SmsSummaryAction exceptionAction = new SmsSummaryAction(string.Empty, string.Format(CultureInfo.CurrentCulture, Utility.TraceException(ex), Text));
                exceptionAction.Status = SmsSummaryAction.ActionStatus.CompleteWithErrors;
                items.Insert(0, exceptionAction);
            }
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                ProgressPageControl progressControl = controlHistory[this.current] as ProgressPageControl;

                if (progressControl != null)
                {
                    progressControl.StopProgress();
                }

                OnNext();
            }
            catch(Exception)
            {
            }
            finally
            {
                BackgroundWorker worker = sender as BackgroundWorker;

                if (worker != null)
                {
                    worker.Dispose();
                }
            }
        }

        private void ProcessCompletion()
        {
            ActionsCompletionWizardPageControl summaryControl = controlHistory[this.current] as ActionsCompletionWizardPageControl;

            if (summaryControl != null)
            {
                summaryControl.OnActivated();
            }
        }

        private void ChangeWizardStep()
        {
            if (Type.GetType((string)current.Tag) == typeof(ActionsSummaryWizardPageControl))
            {
                this.WizardStep = AppEnum.WizardStep.Confirm;
            }
            else if (Type.GetType((string)current.Tag) == typeof(ProgressPageControl))
            {
                this.WizardStep = AppEnum.WizardStep.Progress;
            }
            else if (Type.GetType((string)current.Tag) == typeof(ActionsCompletionWizardPageControl))
            {
                this.WizardStep = AppEnum.WizardStep.Completion;
            }
            else
            {
                this.WizardStep = AppEnum.WizardStep.Custom;
            }
        }

        private void ProcessWizard(AppEnum.WizardStep step)
        {
            switch (step)
            {
                case AppEnum.WizardStep.Custom:
                    {
                    }
                    break;
                case AppEnum.WizardStep.Confirm:
                    {
                        this.ProcessConfirm();
                    }
                    break;
                case AppEnum.WizardStep.Progress:
                    {
                        this.ProcessProgress();
                    }
                    break;
                case AppEnum.WizardStep.Completion:
                    {
                        this.ProcessCompletion();
                    }
                    break;
                default:
                    {
                    }
                    break;
            }
        }

        private void ChangeButtonStatus(AppEnum.WizardStep step)
        {
            switch (step)
            {
                case AppEnum.WizardStep.Custom:
                    {
                        if (this.treeViewNav.Nodes.IndexOf(this.current) == 0)
                        {
                            this.buttonPrevious.Enabled = false;
                        }
                        else
                        {
                            this.buttonPrevious.Enabled = true;
                        }

                        this.buttonNext.Enabled = true;
                        this.buttonCancel.Enabled = true;
                        this.buttonCancel.Text = Properties.Resources.Cancel;
                    }
                    break;
                case AppEnum.WizardStep.Confirm:
                    {
                        this.buttonPrevious.Enabled = true;
                        this.buttonNext.Enabled = true;
                        this.buttonCancel.Enabled = true;
                        this.buttonCancel.Text = Properties.Resources.Cancel;
                    }
                    break;
                case AppEnum.WizardStep.Progress:
                    {
                        this.buttonPrevious.Enabled = false;
                        this.buttonNext.Enabled = false;
                        this.buttonCancel.Enabled = false;
                        this.buttonCancel.Text = Properties.Resources.Cancel;
                    }
                    break;
                case AppEnum.WizardStep.Completion:
                    {
                        this.buttonPrevious.Enabled = false;
                        this.buttonNext.Enabled = false;
                        this.buttonCancel.Enabled = true;
                        this.buttonCancel.Text = Properties.Resources.Close;
                    }
                    break;
                default:
                    {
                    }
                    break;
            }
        }

        private void ChangeMainControl(TreeNode node)
        {
            if (node == null ||
                node.Tag == null)
            {
                return;
            }

            Type type = Type.GetType((string)node.Tag);

            if (type == null)
            {
                return;
            }

            ChangelistPageControl pageControl;

            if (controlHistory.Keys.Contains(node) == true)
            {
                pageControl = controlHistory[node];
            }
            else
            {
                Assembly assembly = Assembly.GetExecutingAssembly();

                pageControl = assembly.CreateInstance(type.FullName) as ChangelistPageControl;

                if (pageControl == null)
                {
                    return;
                }

                pageControl.ChangelistInfo = this.ChangelistInfo;
                pageControl.InitializePageControl();
                controlHistory[node] = pageControl;
            }

            pageControl.Size = this.panelMain.Size;
            this.panelMain.Controls.Clear();
            this.panelMain.Controls.Add(pageControl);
        }

        private void CommitForm_Load(object sender, EventArgs e)
        {
            this.Current = this.treeViewNav.Nodes[0];

            TreeNode confirm = new TreeNode()
            {
                Name = "NodeConfirm",
                Text = Properties.Resources.Confirm,
                Tag = typeof(ActionsSummaryWizardPageControl).ToString()
            };
            this.treeViewNav.Nodes.Add(confirm);

            TreeNode progress = new TreeNode()
            {
                Name = "NodeProgress",
                Text = Properties.Resources.Progress,
                Tag = typeof(ProgressPageControl).ToString()
            };
            this.treeViewNav.Nodes.Add(progress);

            TreeNode completion = new TreeNode()
            {
                Name = "NodeCompletion",
                Text = Properties.Resources.Completion,
                Tag = typeof(ActionsCompletionWizardPageControl).ToString()
            };
            this.treeViewNav.Nodes.Add(completion);

            this.controlHistory.Add(confirm, new ActionsSummaryWizardPageControl());
            this.controlHistory.Add(progress, new ProgressPageControl());
            this.controlHistory.Add(completion, new ActionsCompletionWizardPageControl());
        }

        private void buttonPrevious_Click(object sender, EventArgs e)
        {
            if (this.Current.PrevVisibleNode == null)
            {
                return;
            }

            this.Current = this.Current.PrevVisibleNode;
        }

        private void buttonNext_Click(object sender, EventArgs e)
        {
            this.OnNext();
        }

        private void OnNext()
        {
            ChangelistPageControl currentPage = controlHistory[this.Current];

            if (currentPage.ValidatePage() == false)
            {
                return;
            }

            if (this.Current.NextVisibleNode == null)
            {
                return;
            }

            this.Current = this.Current.NextVisibleNode;
        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            foreach (ChangelistPageControl page in this.controlHistory.Values)
            {
                if (page != null)
                {
                    page.Dispose();
                }
            }

            this.Close();
        }

        private void treeViewNav_BeforeSelect(object sender, TreeViewCancelEventArgs e)
        {
            TreeNode node = e.Node;

            if (node == null)
            {
                return;
            }

            if (this.selectedByControl == false)
            {
                e.Cancel = true;
            }
            else
            {
                this.selectedByControl = false;
            }
        }

        private void CommitForm_Resize(object sender, EventArgs e)
        {
            foreach (ChangelistPageControl var in this.controlHistory.Values)
            {
                var.Size = this.panelMain.Size;
            }
        }

        #region Summary Items

        private void GetSummary()
        {
            // Start from scratch
            items.Clear();

            // Add the summary items.
            OnAddSummary(HandleSummaryRequest);
        }

        /// <summary>
        /// Called when it is time to add Summary Actions.
        /// May override if special handling is required.
        /// </summary>
        protected virtual void OnAddSummary(SummaryRequestHandler handler)
        {
            // Call each of the pages.
            foreach(ChangelistPageControl var in this.controlHistory.Values)
            {
                OnAddSummary(handler, var);
            }
        }

        private void OnAddSummary(SummaryRequestHandler handler, ChangelistPageControl page)
        {
            if (page == null)
            {
                return;
            }

            page.OnAddSummary(handler);
        }

        /// <summary>
        /// This is the delegate passed to the wizard pages and page controls.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="id">The id.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        internal object HandleSummaryRequest(SummaryRequestType request, string id, params object[] parameters)
        {
            // Route the request to the correct method.
            // If not enough parameters were passed, then an IndexOutOfRangeException will be thrown.
            switch (request)
            {
                case SummaryRequestType.GetSummaryItems:
                    return SummaryItems;

                case SummaryRequestType.AddGroup:
                    return AddActionGroup(id, parameters[0] as string);

                case SummaryRequestType.AddAction:
                    return AddAction(id, parameters[0] as string, parameters[1] as IEnumerable<string>, parameters[2] as string);

                case SummaryRequestType.RemoveItem:
                    return RemoveItem(id);

                case SummaryRequestType.ReplaceAction:
                    return ReplaceAction(id, parameters[0] as string, parameters[1] as IEnumerable<string>);

                case SummaryRequestType.UpdateActionStatus:
                    return UpdateActionStatus(id, ((SmsSummaryAction.ActionStatus)parameters[0]));

                case SummaryRequestType.ClearActionDetails:
                    return ClearActionDetails(id);

                case SummaryRequestType.AddActionDetail:
                    return AddActionDetailMessage(id, parameters[0] as string);

                case SummaryRequestType.AddActionWarning:
                    return AddActionWarningMessage(id, parameters[0] as string);

                case SummaryRequestType.AddActionError:
                    return AddActionErrorMessage(id, parameters[0] as string);

                case SummaryRequestType.AddActionCorrectiveStep:
                    return AddActionCorrectiveStepMessage(id, parameters[0] as string);

                case SummaryRequestType.UpdateAction:
                    return UpdateAction(id, parameters[0] as string);

                case SummaryRequestType.UpdateGroup:
                    return UpdateGroup(id, parameters[0] as string);

                case SummaryRequestType.GetSummaryMessage:
                    return summaryMessage;

                case SummaryRequestType.SetSummaryMessage:
                    summaryMessage = parameters[0] as string;
                    return null;

                case SummaryRequestType.GetProceedMessage:
                    return proceedMessage;

                case SummaryRequestType.SetProceedMessage:
                    proceedMessage = parameters[0] as string;
                    return null;

                case SummaryRequestType.GetCloseMessage:
                    return closeMessage;

                case SummaryRequestType.SetCloseMessage:
                    closeMessage = parameters[0] as string;
                    return null;

                default:
                    // The type is not supported.
                    throw new ArgumentException("Invalid request type.");
            }
        }

        private string summaryMessage;
        private string proceedMessage;
        private string closeMessage;
        private List<SmsSummaryItem> items = new List<SmsSummaryItem>();

        /// <summary>
        /// Find the item with the specified id.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public SmsSummaryItem GetItem(string id)
        {
            // I would love to use the List<>'s Find functionality, 
            //  but it wouldn't find the items that are in groups.

            // Iterate through each item in the list.
            foreach (SmsSummaryItem var in items)
            {
                // See if the item can find the specified item.
                SmsSummaryItem item = var.FindItem(id);
                if (item != null)
                {
                    // Found it!!!
                    return item;
                }
            }

            // The item with the specified id was not found.
            throw new ArgumentException("Specified item not found.");
        }

        /// <summary>
        /// Get the list of SummaryActions
        /// </summary>
        protected IEnumerable<SmsSummaryItem> SummaryItems
        {
            get { return items; }
        }

        /// <summary>
        /// Creates a new SmsSummaryActionGroup and adds it to the list.
        /// </summary>
        /// <param name="groupId"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        protected SmsSummaryActionGroup AddActionGroup(string groupId, string title)
        {
            SmsSummaryActionGroup group = new SmsSummaryActionGroup(groupId, title);
            items.Add(group);

            return group;
        }

        /// <summary>
        /// Adds to the specified group, a new action with the specified actionId, title and details.
        /// </summary>
        /// <param name="actionId"></param>
        /// <param name="groupId"></param>
        /// <param name="title"></param>
        /// <param name="details"></param>
        protected SmsSummaryAction AddAction(string actionId, string title, IEnumerable<string> details, string groupId)
        {
            // Create the new action.
            SmsSummaryAction action = new SmsSummaryAction(actionId, title, details);

            // See if a groupId was specified
            if (string.IsNullOrEmpty(groupId))
            {
                // Add it to the base list.
                items.Add(action);
            }
            else
            {
                // Get the group.
                SmsSummaryActionGroup group = (SmsSummaryActionGroup)GetItem(groupId);

                // Add it to the group.
                group.Add(action);
            }

            return action;
        }

        /// <summary>
        /// Removes the specified item.
        /// The item may reside directly in the list, or it may reside inside of a group.
        /// </summary>
        /// <param name="actionId"></param>
        /// <returns></returns>
        protected SmsSummaryItem RemoveItem(string actionId)
        {
            foreach (SmsSummaryItem var in items)
            {
                SmsSummaryItem item = var.FindItem(actionId);
                if (item != null)
                {
                    // Make sure this is an SmsSummaryAction.
                    SmsSummaryAction summaryActionItem = item as SmsSummaryAction;

                    if (summaryActionItem == null)
                    {
                        throw new InvalidCastException();
                    }

                    // The item is in a group.
                    SmsSummaryActionGroup group = var as SmsSummaryActionGroup;
                    if (group != null)
                    {
                        // Remove it.
                        group.Remove(summaryActionItem);
                    }
                    else
                    {
                        // The item is directly in the list.
                        // Remove it.
                        items.Remove(var);
                    }

                    return item;
                }
            }

            // Never found the item to replace.
            throw new ArgumentException("Item not found for specified actionId");
        }

        /// <summary>
        /// Replaces the action with the specified actionId, with a new action with the same actionId, but a new title and details.
        /// </summary>
        /// <param name="actionId"></param>
        /// <param name="title"></param>
        /// <param name="details"></param>
        protected SmsSummaryAction ReplaceAction(string actionId, string title, IEnumerable<string> details)
        {
            foreach (SmsSummaryItem var in items)
            {
                SmsSummaryItem item = var.FindItem(actionId);
                if (item != null)
                {
                    // Make sure this is an SmsSummaryAction.
                    SmsSummaryAction actionOld = item as SmsSummaryAction;
                    if (actionOld == null)
                    {
                        throw new InvalidCastException();
                    }

                    // Create the new action.
                    SmsSummaryAction actionNew = new SmsSummaryAction(actionId, title, details);

                    SmsSummaryActionGroup group = var as SmsSummaryActionGroup;
                    if (group != null)
                    {
                        // Insert the new item where the old one currently resides.
                        group.Insert(group.IndexOf(actionOld), actionNew);

                        // Remove the old one.
                        group.Remove(actionOld);
                    }
                    else
                    {
                        // The item is directly in the list.

                        // Insert the new item where the old one currently resides.
                        items.Insert(items.IndexOf(var), actionNew);

                        // Remove the old one.
                        items.Remove(var);
                    }

                    return actionNew;
                }
            }

            // Never found the item to replace.
            throw new ArgumentException("Item not found for specified actionId");
        }

        /// <summary>
        /// Updates the title of the action with the specified ID.
        /// </summary>
        /// <param name="actionId"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        protected SmsSummaryAction UpdateAction(string actionId, string title)
        {
            // Get the action.
            // This will throw InvalidCastException if the item exists, but is not an SmsSummaryAction.
            SmsSummaryAction action = (SmsSummaryAction)GetItem(actionId);

            // Update the action's title.
            action.Title = title;

            return action;
        }

        /// <summary>
        /// Updates the title of the group with the specified ID.
        /// </summary>
        /// <param name="groupId"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        protected SmsSummaryActionGroup UpdateGroup(string groupId, string title)
        {
            // Get the action.
            // This will throw InvalidCastException if the item exists, but is not an SmsSummaryAction.
            SmsSummaryActionGroup group = (SmsSummaryActionGroup)GetItem(groupId);

            // Update the group's title.
            group.Title = title;

            return group;
        }

        /// <summary>
        /// Updates the status of the action with the specified ID.
        /// </summary>
        /// <param name="actionId"></param>
        /// <param name="status"></param>
        /// <returns></returns>
        protected SmsSummaryAction UpdateActionStatus(string actionId, SmsSummaryAction.ActionStatus status)
        {
            // Get the action.
            // This will throw InvalidCastException if the item exists, but is not an SmsSummaryAction.
            SmsSummaryAction action = (SmsSummaryAction)GetItem(actionId);

            // Update the action's status.
            action.Status = status;

            return action;
        }

        /// <summary>
        /// Sets the details of the specified action.
        /// </summary>
        /// <param name="actionId"></param>
        protected SmsSummaryAction ClearActionDetails(string actionId)
        {
            // Get the action.
            // This will throw InvalidCastException if the item exists, but is not an SmsSummaryAction.
            SmsSummaryAction action = (SmsSummaryAction)GetItem(actionId);

            // Reset the action's Details.
            action.ClearDetails();

            return action;
        }

        /// <summary>
        /// Adds a detail string to the action with the specified ID.
        /// </summary>
        /// <param name="actionId"></param>
        /// <param name="message"></param>
        protected SmsSummaryAction AddActionDetailMessage(string actionId, string message)
        {
            // Get the action.
            // This will throw InvalidCastException if the item exists, but is not an SmsSummaryActionGroup.
            SmsSummaryAction action = (SmsSummaryAction)GetItem(actionId);

            // Add the detail
            action.AddDetail(message);

            return action;
        }

        /// <summary>
        /// Adds a warning message to the action with the specified ID.
        /// </summary>
        /// <param name="actionId"></param>
        /// <param name="message"></param>
        protected SmsSummaryAction AddActionWarningMessage(string actionId, string message)
        {
            // Get the action.
            // This will throw InvalidCastException if the item exists, but is not an SmsSummaryActionGroup.
            SmsSummaryAction action = (SmsSummaryAction)GetItem(actionId);

            // Add the warning
            action.AddWarning(message);

            return action;
        }

        /// <summary>
        /// Adds an error message to the action with the corresponding ID.
        /// </summary>
        /// <param name="actionId"></param>
        /// <param name="message"></param>
        protected SmsSummaryAction AddActionErrorMessage(string actionId, string message)
        {
            // Get the action.
            // This will throw InvalidCastException if the item exists, but is not an SmsSummaryActionGroup.
            SmsSummaryAction action = (SmsSummaryAction)GetItem(actionId);

            // Add the error
            action.AddError(message);

            return action;
        }

        /// <summary>
        /// Adds a correction string to the action with the specified ID.
        /// </summary>
        /// <param name="actionId"></param>
        /// <param name="message"></param>
        protected SmsSummaryAction AddActionCorrectiveStepMessage(string actionId, string message)
        {
            // Get the action.
            // This will throw InvalidCastException if the item exists, but is not an SmsSummaryActionGroup.
            SmsSummaryAction action = (SmsSummaryAction)GetItem(actionId);

            // Add the corrective step
            action.AddCorrectiveStep(message);

            return action;
        }

        #endregion


        #region Font

        public virtual bool GetEnvironmentFont(ref Font font)
        {
            // Release the old font.
            if (font != null)
            {
                font.Dispose();
            }

            // Get the latest font.
            font = SystemFonts.MenuFont;

            return true;
        }

        /// <summary>
        /// This method is called when the system changes the colors.
        /// </summary>
        /// <param name="e">The EventArgs parameter.</param>
        protected override void OnSystemColorsChanged(EventArgs e)
        {
            WizardSettings.CreateFonts(this);
            WizardSettings.InitFontRegular(this);
            base.OnSystemColorsChanged(e);
        }

        #endregion
    }
}
